Skip to content

feat(webauthn): largeBlob write and delete#224

Draft
AlfioEmanueleFresta wants to merge 3 commits into
feat/webauthn-largeblob-storagefrom
feat/webauthn-largeblob-write
Draft

feat(webauthn): largeBlob write and delete#224
AlfioEmanueleFresta wants to merge 3 commits into
feat/webauthn-largeblob-storagefrom
feat/webauthn-largeblob-write

Conversation

@AlfioEmanueleFresta
Copy link
Copy Markdown
Member

Summary

Adds the WebAuthn L3 §10.1.5 largeBlob.write extension and a libwebauthn-side delete operation, layered on top of CTAP 2.2 §6.10.6. The platform fetches the existing on-authenticator array, replaces or erases the entry that decrypts under the credential's largeBlobKey, and re-uploads the canonical CBOR array via chunked authenticatorLargeBlobs(set). Foreign entries (other credentials' blobs, unknown CBOR fields) are preserved byte-for-byte per the §6.10.2 platform contract.

Stacked on #206 (read leg). Merge that first.

Scope

  • New Write(Vec<u8>) and Delete variants on the get-assertion largeBlob extension input
  • written flag on the assertion's largeBlob output
  • pinUvAuthParam for each chunk derived per §6.10.2: authenticate(token, 32×0xff || h'0c00' || u32_le(offset) || SHA-256(set))
  • lbw permission negotiated into the get-assertion token when write or delete is requested, so one PIN/UV ceremony covers both operations. Unprotected authenticators continue to accept the write without auth params per §6.10.2 line 137
  • L3 mutual-exclusion enforced: read+write together returns a NotSupportedError, and write/delete require exactly one allowCredentials entry
  • Delete with no matching entry reports written=false, matching the strict §6.10.6 "return an error" branch

Tests

  • Unit tests cover the pinUvAuthParam byte construction, AAD format, the read-modify-write loop, and the byte-fingerprint of the canonical empty array (h'8076be8b528d0075f7aae98d6fa57a6d3c')
  • Integration tests against the virt authenticator: write then read round trip, write replaces existing, delete erases, delete with no prior entry reports a failure flag

Follow-ups

  • Optional W3C webauthn#2337-style UV auto-upgrade for write/delete on PIN-protected devices under UV=Discouraged. Currently the platform falls through to written=false rather than forcing a PIN prompt
  • §6.10.7 garbage collection of orphan blobs after credential deletion is not implemented

Add new_set_first and new_set_continuation per CTAP 2.2 §6.10.2:
length is present only when offset is zero, omitted otherwise.
Both take Option<(auth_param, protocol)> so unprotected
authenticators can issue the command without pinUvAuthParam.
Implements WebAuthn L3 §10.1.5 write and a libwebauthn-side delete
on top of CTAP 2.2 §6.10.6 update-or-erase. The platform fetches the
existing large-blob array, drops any entry that decrypts under the
credential's largeBlobKey, optionally appends a fresh encrypted entry,
and re-uploads the canonical CBOR array with a fresh SHA-256 trailer.
Foreign entries (different key, malformed, unknown fields) are
preserved byte-for-byte per the §6.10.2 platform contract.

Upload is chunked per maxFragmentLength with pinUvAuthParam computed
as authenticate(token, 32*0xff || 0x0c 0x00 || u32_le(offset)
|| SHA-256(set)) per §6.10.2. The Ctap2GetAssertionRequest now ORs
LARGE_BLOB_WRITE into its permissions when write or delete is
requested so user_verification negotiates a token covering the lbw
permission.

To keep PIN-protected devices working under UV=Discouraged, the
Ctap2UserVerifiableRequest trait gains needs_pin_uv_auth_token,
which suppresses the OnlyForSharedSecret downgrade in
user_verification. Unprotected authenticators continue to accept the
write without auth params per spec line 137.

Delete with no matching entry returns LargeBlobError::NoMatch
(written=false), matching the strict §6.10.6 'Return an error' branch
at line 303.
Exercises the WebAuthn write/delete extensions against the virt
authenticator end-to-end:
- write then read returns the planted bytes
- second write replaces the first entry
- delete after write removes the entry; subsequent read returns no blob
- delete with no prior entry reports written=false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant